package com.cw.lang.common.framework.rpc;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.cw.base.common.model.dto.base.BaseReqDto;
import com.cw.base.common.model.dto.base.BaseUserReqDto;
import com.cw.base.common.model.enums.EnumModuleCode;
import com.cw.base.common.model.enums.EnumServiceScene;
import com.cw.base.common.model.util.AppParamUtil;
import com.cw.lang.common.exception.BizException;
import com.cw.lang.common.framework.aop.annotation.IntegrationHandler;
import com.cw.lang.common.framework.enums.EnumRpcError;
import com.cw.lang.common.framework.utils.RpcAppParamUtil;
import com.cw.lang.common.utils.SequenceTool;

/**
 * @author chenw
 * @version $$Id: IntegrationHandlerAspect.java, v 0.1 2018/9/21 上午9:20 chenw Exp $$
 */
@Order(Ordered.HIGHEST_PRECEDENCE + 2000)
@Aspect
public class RequestIntegrationHandlerAspect {

    /** spEL表达式解析器 */
    private ExpressionParser parser = new SpelExpressionParser();

    /**
     * aop处理
     *
     * @param jp jp
     */
    @Around(value = "@annotation(com.cw.lang.common.framework.aop.annotation.IntegrationHandler)")
    public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable {
        // 塞BaseRequest
        putBaseRequest(jp);
        Object result = jp.proceed();
        // 清理BaseRequest
        clearBaseRequest();
        return result;
    }

    /**
     * 清理线程上下文
     */
    private void clearBaseRequest() {
        RpcAppParamUtil.removeRpcBaseRequest();
        RpcAppParamUtil.removeRpcBaseUserRequest();
    }

    /**
     * 组装{@link BaseReqDto}并塞到线程上下文中
     *
     * @param jp jp
     */
    private void putBaseRequest(JoinPoint jp) {
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();
        BaseReqDto baseReqDto = BaseReqDto.Builder.getInstance()
            .addAsk(getAsk(signature))
            .addAnswer(getAnswer(signature))
            .addServiceScene(getServiceScene(signature))
            .addIdemSerialNo(getIdemSerialNo(method, jp.getTarget(), jp.getArgs()))
            .addTransDateTime(new Date())
            .addChannelNo(AppParamUtil.getChannelNo())
            .addSerialNo(AppParamUtil.getSerialNo())
            .build();
        if (AppParamUtil.getBaseRequest() != null) {
            baseReqDto.setSign(AppParamUtil.getBaseRequest()
                .getSign());
        }
        RpcAppParamUtil.setRpcBaseRequest(baseReqDto);
        if (AppParamUtil.getBaseUserRequest() != null) {
            BaseUserReqDto userRequest = BaseUserReqDto.Builder.getInstance()
                .addAuthId(AppParamUtil.getBaseUserRequest()
                    .getAuthId())
                .addToken(AppParamUtil.getBaseUserRequest()
                    .getToken())
                .addOrganName(AppParamUtil.getOrganName())
                .addOrganId(AppParamUtil.getOrganId())
                .addOperatorName(AppParamUtil.getOperatorName())
                .addOperatorId(AppParamUtil.getOperatorId())
                .build();
            RpcAppParamUtil.setRpcBaseUserRequest(userRequest);
        }
    }

    /**
     * 获取ask
     *
     * @param signature signature
     * @return {@link EnumModuleCode}
     */
    private EnumModuleCode getAsk(Signature signature) {
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();
        IntegrationHandler methodAnnotation = method.getAnnotation(IntegrationHandler.class);
        if (EnumModuleCode.UNKNOWN == methodAnnotation.ask()) {
            Annotation classAnnotation = signature.getDeclaringType()
                .getAnnotation(IntegrationHandler.class);
            IntegrationHandler classIntegrationHandler = (IntegrationHandler) classAnnotation;
            if (EnumModuleCode.UNKNOWN == classIntegrationHandler.ask()) {
                throw new BizException(EnumRpcError.ASK_CAN_NOT_BE_UNKNOWN.getCode(),
                    EnumRpcError.ASK_CAN_NOT_BE_UNKNOWN.getDescription());
            } else {
                return classIntegrationHandler.ask();
            }
        } else {
            return methodAnnotation.ask();
        }
    }

    /**
     * 获取answer
     *
     * @param signature signature
     * @return {@link EnumModuleCode}
     */
    private EnumModuleCode getAnswer(Signature signature) {
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();
        IntegrationHandler methodAnnotation = method.getAnnotation(IntegrationHandler.class);
        if (EnumModuleCode.UNKNOWN == methodAnnotation.answer()) {
            Annotation classAnnotation = signature.getDeclaringType()
                .getAnnotation(IntegrationHandler.class);
            IntegrationHandler classIntegrationHandler = (IntegrationHandler) classAnnotation;
            if (EnumModuleCode.UNKNOWN == classIntegrationHandler.answer()) {
                throw new BizException(EnumRpcError.ANSWER_CAN_NOT_BE_UNKNOWN.getCode(),
                    EnumRpcError.ANSWER_CAN_NOT_BE_UNKNOWN.getDescription());
            } else {
                return classIntegrationHandler.answer();
            }
        } else {
            return methodAnnotation.answer();
        }
    }

    /**
     * 获取幂等流水号
     *
     * @param method 被{@link IntegrationHandler}注解的方法
     * @param target method 所在的对象
     * @param args 方法的入参
     * @return 幂等流水号
     */
    private String getIdemSerialNo(Method method, Object target, Object[] args) {
        IntegrationHandler methodAnnotation = method.getAnnotation(IntegrationHandler.class);
        String idemSerialNo = methodAnnotation.idemSerialNo();
        // 如果注解中幂等流水号没有传，则自动生成一个
        if (StringUtils.isBlank(idemSerialNo)) {
            return SequenceTool.nextId();
        }
        // 如果幂等流水号传了，则使用spel表达式进行解析
        else {
            Expression expression = parser.parseExpression(methodAnnotation.idemSerialNo());
            MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(target, method, args,
                new DefaultParameterNameDiscoverer());
            Object tradeId = expression.getValue(evaluationContext);
            if (tradeId == null) {
                throw new BizException(EnumRpcError.IDEM_SERIAL_NO_NOT_SUPPORT.getCode(),
                    EnumRpcError.IDEM_SERIAL_NO_NOT_SUPPORT.getDescription());
            } else {
                if (tradeId instanceof String) {
                    return (String) tradeId;
                } else {
                    throw new BizException(EnumRpcError.IDEM_SERIAL_NO_NOT_SUPPORT_CLASS.getCode(),
                        EnumRpcError.IDEM_SERIAL_NO_NOT_SUPPORT_CLASS.getDescription(), String.class.getName(),
                        tradeId.getClass()
                            .getName());
                }
            }
        }
    }

    /**
     * 获取服务场景
     *
     * @param signature signature
     * @return {@link EnumServiceScene}
     */
    private EnumServiceScene getServiceScene(Signature signature) {
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();
        IntegrationHandler methodAnnotation = method.getAnnotation(IntegrationHandler.class);
        if (EnumServiceScene.UNKNOWN == methodAnnotation.serviceScene()) {
            throw new BizException(EnumRpcError.SERVICE_SCENE_CAN_NOT_BE_UNKNOWN.getCode(),
                EnumRpcError.SERVICE_SCENE_CAN_NOT_BE_UNKNOWN.getDescription());
        } else {
            return methodAnnotation.serviceScene();
        }
    }
}
